using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using EzPos.DataAccess.SaleOrder;
using EzPos.Model.Common;
using EzPos.Model.SaleOrder;
using EzPos.Properties;
using EzPos.Utility;

namespace EzPos.Service.SaleOrder
{
    public class SaleOrderService
    {
        private readonly SaleOrderDataAccess _saleOrderDataAccess;

        public SaleOrderService(SaleOrderDataAccess saleOrderDataAccess)
        {
            _saleOrderDataAccess = saleOrderDataAccess;
        }

        public virtual IList GetSaleOrders()
        {
            return _saleOrderDataAccess.GetSaleOrders();
        }

        public virtual IList GetSaleOrders(IList searchCriteria)
        {
            if (searchCriteria == null)
                throw new ArgumentNullException("searchCriteria", Resources.MsgInvalidSearchCriteria);

            return _saleOrderDataAccess.GetSaleOrders(searchCriteria);
        }

        //public virtual Model.SaleOrder.SaleOrder GetSaleOrder(Model.Deposit.Deposit deposit)
        //{
        //    if (deposit == null)
        //        throw new ArgumentNullException("deposit", Resources.MsgInvalidDeposit);

        //    var saleOrder = 
        //        new Model.SaleOrder.SaleOrder
        //        {
        //            AmountPaidForeign = deposit.AmountPaidForeign,
        //            AmountPaidLocal = deposit.AmountPaidLocal,
        //            AmountReturnForeign = deposit.AmountReturnForeign,
        //            AmountReturnLocal = deposit.AmountReturnLocal,
        //            AmountSoldForeign = deposit.AmountSoldForeign,
        //            AmountSoldLocal = deposit.AmountSoldLocal,
        //            CardNumber = deposit.CardNumber,
        //            CashierId = deposit.CashierId,
        //            CurrencyId = deposit.CurrencyId,
        //            CustomerId = deposit.CustomerId,
        //            DelivererId = deposit.DelivererId,
        //            Description = deposit.Description,
        //            Discount = deposit.Discount,
        //            DiscountTypeId = deposit.DiscountTypeId,
        //            ExchangeRate = deposit.ExchangeRate,
        //            FkCustomer = deposit.FkCustomer,
        //            PaymentTypeId = deposit.PaymentTypeId,
        //            ReferenceNum = deposit.ReferenceNum,
        //            SaleOrderDate = deposit.DepositDate,
        //            SaleOrderTypeId = deposit.DepositTypeId
        //        };
        //    return saleOrder;
        //}

        public virtual IList GetSaleHistories(IList searchCriteria)
        {
            if (searchCriteria == null)
                throw new ArgumentNullException("searchCriteria", Resources.MsgInvalidSearchCriteria);

            var saleOrderReportList = _saleOrderDataAccess.GetSaleHistories(searchCriteria);
            foreach (SaleOrderReport saleOrderReport in saleOrderReportList)
            {
                if (!string.IsNullOrEmpty(saleOrderReport.ReferenceNum))
                    saleOrderReport.SaleOrderNumber += " (" + saleOrderReport.ReferenceNum + ")";

                if (!string.IsNullOrEmpty(saleOrderReport.CardNumber))
                    saleOrderReport.CustomerName += " (" + saleOrderReport.CardNumber + ")";
            }

            return saleOrderReportList;
        }

        public virtual void SaleOrderManagement(Model.SaleOrder.SaleOrder saleOrder, string requestCode)
        {
            if (requestCode == null)
                throw new ArgumentNullException("requestCode", Resources.MsgInvalidRequest);

            if (saleOrder == null)
                throw new ArgumentNullException("SaleOrder", Resources.MsgInvalidSaleOrder);

            if (requestCode == Resources.OperationRequestInsert)
                InsertSaleOrder(saleOrder);
            else if (requestCode == Resources.OperationRequestDuplicate)
            {
                saleOrder.SaleOrderId = 0;
                InsertSaleOrder(saleOrder);
            }
            else if (requestCode == Resources.OperationRequestUpdate)
                UpdateSaleOrder(saleOrder);
            else if (requestCode == Resources.OperationRequestDelete)
                DeleteSaleOrder(saleOrder);
            else if (requestCode == Resources.OperationRequestCancel)
                CancelSaleOrder(saleOrder);
        }

        private void CancelSaleOrder(Model.SaleOrder.SaleOrder saleOrder)
        {
            if (saleOrder == null)
                throw new ArgumentNullException("SaleOrder", Resources.MsgInvalidSaleOrder);
            
            var saleItemList = _saleOrderDataAccess.GetSaleItems(saleOrder.SaleOrderId);
            foreach (var saleItem in saleItemList.Cast<SaleItem>().Where(saleItem => saleItem != null))
            {
                saleItem.QtySold = 0;                
                _saleOrderDataAccess.UpdateSaleItem(saleItem);
            }

            saleOrder.SaleOrderTypeId = 1;
            UpdateSaleOrder(saleOrder);
        }

        private void DeleteSaleOrder(Model.SaleOrder.SaleOrder saleOrder)
        {
            _saleOrderDataAccess.DeleteSaleOrder(saleOrder);
        }

        private void UpdateSaleOrder(Model.SaleOrder.SaleOrder saleOrder)
        {
            _saleOrderDataAccess.UpdateSaleOrder(saleOrder);
        }

        private void InsertSaleOrder(Model.SaleOrder.SaleOrder saleOrder)
        {
            if (saleOrder == null)
                throw new ArgumentNullException("saleOrder", Resources.MsgInvalidSaleOrder);

            //Reset invoice
            var searchCriteria = new List<string>
                                     {
                                         "ParameterTypeId|" + Resources.AppParamResetInvoice
                                     };
            var commonService = ServiceFactory.GenerateServiceInstance().GenerateCommonService();
            var appParameterList = commonService.GetAppParameters(searchCriteria);

            var saleOrderNumber = "S-00001";
            if ((appParameterList != null) && (appParameterList.Count != 0))
            {
                var appParameter = (AppParameter)appParameterList[0];
                if(appParameter != null)
                {
                    if (appParameter.ParameterValue.Equals("True"))
                    {
                        appParameter.ParameterValue = "False";
                        commonService.AppParameterManagement(
                            appParameter,
                            Resources.OperationRequestUpdate);
                    }
                    else
                    {
                        var lastSaleOrder = _saleOrderDataAccess.GetLastSaleOrder();
                        if(lastSaleOrder != null)
                        {
                            var lastSaleOrderNumber = lastSaleOrder.SaleOrderNumber;
                            lastSaleOrderNumber = StringHelper.Right(lastSaleOrderNumber, lastSaleOrderNumber.Length - 2);

                            saleOrderNumber =
                                "S-" +
                                StringHelper.Right(
                                    "00000" + StringHelper.Increment(lastSaleOrderNumber, 1),
                                    5);
                        }
                    }
                }
            }

            //_saleOrderDataAccess.InsertSaleOrder(saleOrder);
            //saleOrder.SaleOrderNumber = "S-00" + saleOrder.SaleOrderId;
            //_saleOrderDataAccess.UpdateSaleOrder(saleOrder);

            saleOrder.SaleOrderNumber = saleOrderNumber;
            _saleOrderDataAccess.InsertSaleOrder(saleOrder);
        }

        public virtual Model.SaleOrder.SaleOrder RecordSaleOrder(
            IList saleItemList,
            float totalAmountInt,
            float totalAmountPaidInt,
            float totalAmountPaidRiel,
            float totalAmountReturnInt,
            Model.Customer.Customer customer,
            bool isReturned,
            string referenceNum,
            float discount,
            float depositAmount,
            bool fromDeposit)
        {
            if (saleItemList == null)
                throw new ArgumentNullException("saleItemList", Resources.MsgInvalidSaleItem);

            var factor = 1;
            if (isReturned)
                factor = -1;

            //SaleOrder
            var saleOrder = 
                new Model.SaleOrder.SaleOrder
                {
                    SaleOrderDate = DateTime.Now,
                    SaleOrderTypeId = (isReturned ? 1 : 0),
                    CustomerId = customer.CustomerId,
                    CashierId = AppContext.User.UserId,
                    DelivererId = 0,
                    Description = "",
                    PaymentTypeId = 0,
                    CurrencyId = 0,
                    ExchangeRate = (AppContext.ExchangeRate == null ? 0 : AppContext.ExchangeRate.ExchangeValue),
                    AmountSoldForeign = fromDeposit ? totalAmountInt : (totalAmountInt - ((totalAmountInt * discount) / 100)),
                    DepositAmount = depositAmount
                };

            saleOrder.AmountSoldForeign *= factor;
            saleOrder.AmountSoldLocal = saleOrder.AmountSoldForeign * saleOrder.ExchangeRate;
            if (isReturned)
            {
                saleOrder.AmountPaidForeign = saleOrder.AmountSoldForeign;
                saleOrder.AmountPaidLocal = 0;
            }
            else
            {
                saleOrder.AmountPaidForeign = totalAmountPaidInt;
                saleOrder.AmountPaidLocal = totalAmountPaidRiel;
            }
            saleOrder.AmountReturnForeign = totalAmountReturnInt;
            saleOrder.AmountReturnLocal = saleOrder.AmountReturnForeign * saleOrder.ExchangeRate;
            saleOrder.Discount = discount;
            if (customer.FkDiscountCard != null)
            {
                saleOrder.DiscountTypeId = customer.FkDiscountCard.DiscountCardTypeId;
                saleOrder.CardNumber = customer.FkDiscountCard.CardNumber;
            }
            saleOrder.ReferenceNum = referenceNum;

            //saleOrder.
            InsertSaleOrder(saleOrder);

            //Customer
            var customerService = ServiceFactory.GenerateServiceInstance().GenerateCustomerService();
            if(fromDeposit)
            {
                customer.PurchasedAmount += saleOrder.AmountPaidForeign;
                customer.DebtAmount += saleOrder.AmountPaidForeign;
            }
            else
                customer.DebtAmount += saleOrder.AmountReturnForeign;
            customerService.CustomerManagement(
                customer,
                Resources.OperationRequestUpdate);

            //localy update SaleOrder
            saleOrder.FkCustomer = customer;
            
            //Sale item      
            var productService = ServiceFactory.GenerateServiceInstance().GenerateProductService();
            var isAllowed = true;
            foreach (var saleItem in saleItemList.Cast<SaleItem>().Where(saleItem => saleItem.ProductId != 0))
            {
                if (isReturned)
                    saleItem.QtySold *= factor;

                //Product                    
                productService.UpdateProduct(saleItem);

                //SaleItem
                saleItem.SaleOrderId = saleOrder.SaleOrderId;
                _saleOrderDataAccess.InsertSaleItem(saleItem);

                var saleOrderReport = 
                    new SaleOrderReport
                        {
                            SalesOrderId = saleOrder.SaleOrderId,
                            SaleOrderNumber = saleOrder.SaleOrderNumber,
                            SaleOrderDate = ((DateTime) saleOrder.SaleOrderDate),
                            CustomerId = customer.CustomerId,
                            CustomerName = customer.CustomerName,
                            CashierName = AppContext.User.LogInName,
                            ExchangeRate = saleOrder.ExchangeRate
                        };

                if (isAllowed)
                {
                    saleOrderReport.AmountSoldForeign = saleOrder.AmountSoldForeign;
                    saleOrderReport.AmountPaidForeign = saleOrder.AmountPaidForeign;
                    saleOrderReport.AmountPaidLocal = saleOrder.AmountPaidLocal;
                    saleOrderReport.AmountReturnForeign = saleOrder.AmountReturnForeign;
                    saleOrderReport.AmountReturnLocal = saleOrder.AmountReturnLocal;
                    saleOrderReport.DiscountTypeId = saleOrder.DiscountTypeId;
                    saleOrderReport.TotalDiscount = saleOrder.Discount;
                    saleOrderReport.CardNumber = saleOrder.CardNumber;
                    saleOrderReport.ReportHeader = 1;
                    saleOrderReport.DepositAmount = depositAmount;
                }
                saleOrderReport.SaleItemId = saleItem.SaleItemId;
                saleOrderReport.ProductId = saleItem.ProductId;
                saleOrderReport.ReferenceNum = referenceNum;                

                if (saleItem.FkProduct != null)
                {
                    saleOrderReport.CategoryStr = saleItem.FkProduct.CategoryStr;
                    saleOrderReport.PurchaseUnitPrice = saleItem.FkProduct.UnitPriceIn;

                    if (!string.IsNullOrEmpty(saleItem.FkProduct.ProductCode))
                    {
                        var productCode = 
                            !string.IsNullOrEmpty(saleItem.FkProduct.ForeignCode) ?
                                                                                      saleItem.FkProduct.ForeignCode :
                                                                                                                         string.Empty;
                        productCode = productCode.Replace(",", string.Empty);
                        productCode = productCode.Replace(" ", string.Empty);
                        saleOrderReport.ProductCode =
                            productCode + 
                            " (" + 
                            (!string.IsNullOrEmpty(saleItem.FkProduct.ProductCode) ? 
                                                                                       saleItem.FkProduct.ProductCode : 
                                                                                                                          string.Empty) + 
                            ")";
                        saleOrderReport.ProductName = saleItem.ProductName + " (" + productCode + ")";                        
                    }
                }

                if (string.IsNullOrEmpty(saleOrderReport.ProductName))
                    saleOrderReport.ProductName = saleItem.ProductName;
                saleOrderReport.UnitPriceIn = saleItem.UnitPriceIn;
                saleOrderReport.Discount = saleItem.Discount;
                saleOrderReport.QtySold = saleItem.QtySold;
                //Public Unit Price Out : Unit price after discount
                saleOrderReport.UnitPriceOut = saleItem.PublicUpOut;
                saleOrderReport.SubTotal = saleItem.PublicUpOut * saleItem.QtySold;

                _saleOrderDataAccess.InsertSaleOrderReport(saleOrderReport);
                isAllowed = false;
            }
            return saleOrder;
        }

        //SaleItem
        public virtual IList GetSaleItems(int saleOrderId)
        {
            var saleItemList = _saleOrderDataAccess.GetSaleItems(saleOrderId);
            return SortSaleItems(saleItemList);
        }

        public virtual IList GetSaleItems(IList searchCriteria)
        {
            var saleItemList = _saleOrderDataAccess.GetSaleItems(searchCriteria);
            return SortSaleItems(saleItemList);
        }

        public IList SortSaleItems(IList saleItemList)
        {
            if(saleItemList == null)
                throw new ArgumentNullException("saleItemList", Resources.MsgInvalidSaleItem);

            var returnSaleItemList = new List<SaleItem>();
            //Food
            var foodSaleItemList =
                from saleItem
                    in saleItemList.Cast<SaleItem>()
                where (saleItem.FkProduct != null) && (saleItem.FkProduct.CategoryId == 481)
                select saleItem;
            returnSaleItemList.AddRange(foodSaleItemList);

            //Desert
            var desertSaleItemList =
                from saleItem
                    in saleItemList.Cast<SaleItem>()
                where (saleItem.FkProduct != null) && (saleItem.FkProduct.CategoryId == 510)
                select saleItem;
            returnSaleItemList.AddRange(desertSaleItemList);

            //Drink
            var drinkSaleItemList =
                from saleItem
                    in saleItemList.Cast<SaleItem>()
                where (saleItem.FkProduct != null) && (saleItem.FkProduct.CategoryId == 599)
                select saleItem;
            returnSaleItemList.AddRange(drinkSaleItemList);

            return returnSaleItemList;
        }

        public virtual void SaleItemManagement(SaleItem saleItem, string requestCode)
        {
            if (requestCode == null)
                throw new ArgumentNullException("requestCode", Resources.MsgInvalidRequest);

            if (saleItem == null)
                throw new ArgumentNullException("saleItem", Resources.MsgInvalidSaleItem);

            if (requestCode == Resources.OperationRequestInsert)
                InsertSaleItem(saleItem);
            else if (requestCode == Resources.OperationRequestDuplicate)
            {
                saleItem.SaleItemId = 0;
                InsertSaleItem(saleItem);
            }
            else if (requestCode == Resources.OperationRequestUpdate)
                UpdateSaleItem(saleItem);
            else
                DeleteSaleItem(saleItem);            
        }

        private void InsertSaleItem(SaleItem saleItem)
        {
            _saleOrderDataAccess.InsertSaleItem(saleItem);    
        }

        private void UpdateSaleItem(SaleItem saleItem)
        {
            _saleOrderDataAccess.UpdateSaleItem(saleItem);    
        }

        private void DeleteSaleItem(SaleItem saleItem)
        {
            _saleOrderDataAccess.DeleteSaleItem(saleItem);
        }

        public void RecordSaleOrderReport(Model.SaleOrder.SaleOrder saleOrder, List<SaleItem> saleItemList)
        {
            if(saleOrder == null)
                throw new ArgumentNullException("saleOrder", Resources.MsgInvalidSaleOrder);

            if(saleItemList == null)
                throw new ArgumentNullException("saleItemList", Resources.MsgInvalidSaleItem);

            var isAllowed = true;
            var customer = saleOrder.FkCustomer ?? new Model.Customer.Customer();
            foreach (var saleItem in saleItemList.Where(saleItem => saleItem != null))
            {
                var saleOrderReport =
                    new SaleOrderReport
                    {
                        SalesOrderId = saleOrder.SaleOrderId,
                        SaleOrderNumber = saleOrder.SaleOrderNumber,
                        SaleOrderDate = ((DateTime)saleOrder.SaleOrderDate),
                        CustomerId = customer.CustomerId,
                        CustomerName = customer.CustomerName,
                        CashierName = AppContext.User.LogInName,
                        ExchangeRate = saleOrder.ExchangeRate
                    };

                if (isAllowed)
                {
                    saleOrderReport.AmountSoldForeign = saleOrder.AmountSoldForeign;
                    saleOrderReport.AmountPaidForeign = saleOrder.AmountPaidForeign;
                    saleOrderReport.AmountPaidLocal = saleOrder.AmountPaidLocal;
                    saleOrderReport.AmountReturnForeign = saleOrder.AmountReturnForeign;
                    saleOrderReport.AmountReturnLocal = saleOrder.AmountReturnLocal;
                    saleOrderReport.DiscountTypeId = saleOrder.DiscountTypeId;
                    saleOrderReport.TotalDiscount = saleOrder.Discount;
                    saleOrderReport.CardNumber = saleOrder.CardNumber;
                    saleOrderReport.ReportHeader = 1;
                    saleOrderReport.DepositAmount = 0;
                }
                saleOrderReport.SaleItemId = saleItem.SaleItemId;
                saleOrderReport.ProductId = saleItem.ProductId;
                saleOrderReport.ReferenceNum = string.Empty;

                if (saleItem.FkProduct != null)
                {
                    saleOrderReport.CategoryStr = saleItem.FkProduct.CategoryStr;
                    saleOrderReport.PurchaseUnitPrice = saleItem.FkProduct.UnitPriceIn;

                    if (!string.IsNullOrEmpty(saleItem.FkProduct.ProductCode))
                    {
                        var productCode =
                            !string.IsNullOrEmpty(saleItem.FkProduct.ForeignCode) ?
                            saleItem.FkProduct.ForeignCode :
                            string.Empty;
                        productCode = productCode.Replace(",", string.Empty);
                        productCode = productCode.Replace(" ", string.Empty);
                        saleOrderReport.ProductCode =
                            productCode +
                            " (" +
                            (!string.IsNullOrEmpty(saleItem.FkProduct.ProductCode) ?
                            saleItem.FkProduct.ProductCode :
                            string.Empty) +
                            ")";
                        saleOrderReport.ProductName = saleItem.ProductName + " (" + productCode + ")";
                    }
                }

                if (string.IsNullOrEmpty(saleOrderReport.ProductName))
                    saleOrderReport.ProductName = saleItem.ProductName;
                saleOrderReport.UnitPriceIn = saleItem.UnitPriceIn;
                saleOrderReport.Discount = saleItem.Discount;
                saleOrderReport.QtySold = saleItem.QtySold;
                //Public Unit Price Out : Unit price after discount
                saleOrderReport.UnitPriceOut = saleItem.PublicUpOut;
                saleOrderReport.SubTotal = saleItem.PublicUpOut * saleItem.QtySold;

                _saleOrderDataAccess.InsertSaleOrderReport(saleOrderReport);
                isAllowed = false;                
            }
        }
    }
}